#define LP_CORE

#include "lp_socketpoll.h"
#include "lp_storage.h"
#include "lp_thread.h"
#include "lp_timer.h"

#ifdef LPSOCK_USE_EPOLL
#include <sys/epoll.h>
#endif

#include <stdlib.h>
#include <memory.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>


#ifdef LPSYS_WIN
#define NOT_WOULDBLOCK (WSAGetLastError() != WSAEWOULDBLOCK) 
#else
#define IS_WOULDBLOCK (errno != EWOULDBLOCK)
#endif


struct LPSocketPoll{
#ifdef LPSOCK_USE_SELECT
    LPSocketEvent * sets;
    LPSocketEvent * end;
    LPSocketEvent * cur;
    LPMutex lock;
#endif

#ifdef LPSOCK_USE_EPOLL
    LPSOCK_FD epollFd;
#endif

#ifdef LPSOCK_USE_KQUEUE
    LPSOCK_FD kqueueFd;
#endif
    uint32_t setsCount;
};


LP_API LPSocketPoll *
lpNewSocketPoll () {
    LPSocketPoll * poll = malloc(sizeof(LPSocketPoll));
    memset(poll, 0, sizeof(LPSocketPoll));

#ifdef LPSOCK_USE_SELECT
    lpInitMutex(&(poll->lock));
#endif

#ifdef LPSOCK_USE_EPOLL
    poll->epollFd = epoll_create(1024);
    if (poll->epollFd == -1){
        free(poll);
        return NULL;
    }
#endif

#ifdef LPSOCK_USE_KQUEUE
    poll->kqueueFd = kqueue();
#endif
    return poll;
}

LP_API void
lpFreeSocketPoll (LPSocketPoll * poll) {
#ifdef LPSOCK_USE_SELECT
    lpLockMutex(&(poll->lock));
    LPSocketEvent * e = poll->sets;
    while(e){
        LPSocketEvent * tmp = e;
        e = e->next;
        free(tmp);
    }
    poll->sets = NULL;
    poll->end = NULL;
    lpClearMutex(&(poll->lock));
#endif

#ifdef LPSOCK_USE_EPOLL
    close(poll->epollFd);
#endif

#ifdef LPSOCK_USE_KQUEUE
    close(poll->kqueueFd);
#endif

    free(poll);
}


LP_API int
lpRegInSocketPoll (LPSocketPoll * poll, 
            LPSOCK_FD fd, int evt, void * ud) {

#ifdef LPSOCK_USE_SELECT
    lpLockMutex(&(poll->lock));
    LPSocketEvent * e = poll->sets;
    while(e){
        if(e->fd == fd){
            e->evt = evt;
            e->ud = ud;
            lpUnlockMutex(&(poll->lock));
            return 0;
        }
        e = e->next;
    }

    e = malloc(sizeof(LPSocketEvent));
    memset(e, 0, sizeof(LPSocketEvent));
    e->fd = fd;
    e->evt = evt;
    e->ud = ud;
    if(poll->end){
        poll->end->next = e;
        poll->end = e;
    }else{
        poll->sets = e;
        poll->end = e;
    }
    poll->setsCount ++;
    lpUnlockMutex(&(poll->lock));
    return 0;
#endif

#ifdef LPSOCK_USE_EPOLL
    poll->setsCount ++;
    struct epoll_event ev;
    ev.events = evt;
    ev.data.ptr = ud;
    if (epoll_ctl(poll->epollFd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        return 1;
    }
#endif

#ifdef LPSOCK_USE_KQUEUE
    poll->setsCount ++;
    struct kevent ke;
    EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, ud);
    if (kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL) == -1 || ke.flags & EV_ERROR) {
        return 1;
    }
    EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, ud);
    if (kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL) == -1 || ke.flags & EV_ERROR) {
        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
        kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL);
        return 1;
    }
    return lpSetEvtInSocketPoll(poll, fd, evt, ud);
#endif

    return 0;
}

LP_API int
lpDelInSocketPoll(LPSocketPoll * poll, LPSOCK_FD fd) {
#ifdef LPSOCK_USE_SELECT
    LPSocketEvent * pre = NULL;
    lpLockMutex(&(poll->lock));
    LPSocketEvent * e = poll->sets;
    while(e){
        if(e->fd == fd){
            if (pre)
                pre->next = e->next;
            else
                poll->sets = e->next;
            if (poll->end == e)
                poll->end = pre;
            poll->setsCount --;
            lpUnlockMutex(&(poll->lock));
            free(e);
            return 0;
        }
        pre = e;
        e = e->next;
    }
     lpUnlockMutex(&(poll->lock));
     assert(0);
    return 1;
#endif

#ifdef LPSOCK_USE_EPOLL
    poll->setsCount --;
    return epoll_ctl(poll->epollFd, EPOLL_CTL_DEL, fd , NULL) == -1;
#endif


#ifdef LPSOCK_USE_KQUEUE
    poll->setsCount --;
    struct kevent ke;
    EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
    kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL);
    EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
    kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL);
#endif
    return 0;
}


LP_API int
lpSetEvtInSocketPoll(LPSocketPoll * poll, LPSOCK_FD fd, int evt, void * ud) {
#ifdef LPSOCK_USE_SELECT
    lpLockMutex(&(poll->lock));
    LPSocketEvent * e = poll->sets;
    while(e){
        if(e->fd == fd){
            e->evt = evt;
            e->ud = ud;
            lpUnlockMutex(&(poll->lock));
            return 0;
        }
        e = e->next;
    }
    lpUnlockMutex(&(poll->lock));
    return 1;
#endif

#ifdef LPSOCK_USE_EPOLL
    struct epoll_event ev;
    ev.events = evt;
    ev.data.ptr = ud;
    epoll_ctl(poll->epollFd, EPOLL_CTL_MOD, fd, &ev);
#endif

#ifdef LPSOCK_USE_KQUEUE
    struct kevent ke;
    int opt = (evt & DDSOCKETPOLL_READ) ? EV_ENABLE : EV_DISABLE;
    EV_SET(&ke, fd, EVFILT_READ, opt, 0, 0, ud);
    kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL);


    opt = (evt & DDSOCKETPOLL_WRITE) ? EV_ENABLE : EV_DISABLE;
    EV_SET(&ke, fd, EVFILT_WRITE, opt, 0, 0, ud);
    kevent(poll->kqueueFd, &ke, 1, NULL, 0, NULL);
#endif

    return 0;
}

#ifdef LPSOCK_USE_SELECT
static int 
selectWait(LPSocketEvent ** e, LPSocketEvent * evts, int max){
    int n = 0;
    int fdsz = 0;
    int maxfd = 0;
    fd_set rd, wd, ed;
    FD_ZERO(&rd); FD_ZERO(&wd); FD_ZERO(&ed);
    LPSocketEvent * cur = *e;
    while(cur){
        if(fdsz >= LP_SOCKPOLL_SETSIZE)
            break;
        fdsz ++;
        if (cur->evt & LPSOCKETPOLL_READ)
            FD_SET(cur->fd, &rd);
        if(cur->evt & LPSOCKETPOLL_WRITE)
            FD_SET(cur->fd, &wd);
        if (cur->evt & LPSOCKETPOLL_ERROR) {
            FD_SET(cur->fd, &ed);
            //int error = 0; 
            //int len = sizeof(int);
            //int ret = getsockopt(cur->fd, SOL_SOCKET, SO_ERROR, &error, &len);
            //if (ret == DDSOCK_INVALID || error) {
            //    cur->evt |= DDSOCKETPOLL_BE_ERROR;
            //    n += 1;
            //}
            //int r = recv(cur->fd, tb, 1, MSG_PEEK);
            //if (r <= 0 && NOT_WOULDBLOCK) {
            //  cur->evt |= DDSOCKETPOLL_BE_ERROR;
            //  n += 1;
            //}
        }
        if(cur->fd >= maxfd)
            maxfd = (int)cur->fd;
        cur = cur->next;
    }
    struct timeval wt = { 0 };
    //tv.tv_sec * 1000 + tv.tv_usec / 1000;
    //wt.tv_sec = 1;
    //wt.tv_usec = 1000;
    if (select(maxfd + 1, &rd, &wd, &ed, &wt) <= 0 && n == 0){
        return 0;
    }

    LPSocketEvent * begin = *e;
    *e = cur;
    LPSocketEvent * result;
    n = 0;
    for(int i = 0; i < fdsz; i ++){
        if(n >= max)
            return n;

        result = &(evts[n]);
        result->evt = 0;
        if((begin->evt & LPSOCKETPOLL_READ) && FD_ISSET(begin->fd, &rd))
            result->evt |= LPSOCKETPOLL_READ;
        if((begin->evt & LPSOCKETPOLL_WRITE) && FD_ISSET(begin->fd, &wd))
            result->evt |= LPSOCKETPOLL_WRITE;
        if(((begin->evt & LPSOCKETPOLL_ERROR) && FD_ISSET(begin->fd, &ed))
            || (begin->evt & LPSOCKETPOLL_BE_ERROR))
            result->evt |= LPSOCKETPOLL_ERROR;
        if (result->evt > 0){
            n ++;
            //result->fd = begin->fd;
            result->ud = begin->ud;
        }
        begin = begin->next;
    }
    return n;

}
#endif

LP_API int
lpWaitSocketPollEvt(LPSocketPoll * poll, LPSocketEvent * evts, int max, int timeout) {
#ifdef LPSOCK_USE_SELECT
    int n = 0;
    uint64_t now = lpTime();
    for(;;){
        lpLockMutex(&(poll->lock));
        LPSocketEvent * e = poll->sets;
        while(e){
            n += selectWait(&e, evts + n, max - n);
            if(n > 0){
                lpUnlockMutex(&(poll->lock));
                return n;
            }
            e = e->next;
        }
        lpUnlockMutex(&(poll->lock));
        if(timeout > 1){
            if((lpTime() - now) >= timeout)
                return n;
        }
        else if(timeout == 1) {
            break;
        }
        lpSleepms(10);
    }
    return n;
#endif

#ifdef LPSOCK_USE_EPOLL
    struct epoll_event evs[max];
    int n = epoll_wait(poll->epollFd, evs, max, timeout);
    if (n <= 0)
        return n;
    LPSocketEvent * e;
    for(int i = 0; i < n; i ++){
        e = &(evts[i]);
        e->fd = 0;
        e->ud = evs[i].data.ptr;
        e->evt = evs[i].events;
    }
    return n;
#endif

#ifdef LPSOCK_USE_KQUEUE
    struct kevent evs[max];
    struct timespec * tp = NULL;
    if(timeout>=0){
        struct timespec t = { timeout / 1000, timeout * 1000000};
        tp = &t;
    }
    int n = kevent(poll->kqueueFd, NULL, 0, evs, max, tp);
    if(n <= 0)
        return n;
    LPSocketEvent * e;
    int evt = 0;
    for(int i = 0; i < n; i ++){
        e = &(evts[i]);
        e->fd = 0;
        e->ud = evs[i].udata;
        if(evs[i].filter == -2){
            e->evt = DDSOCKETPOLL_WRITE;
        }else if(evs[i].filter == -1){
            e->evt = DDSOCKETPOLL_READ;
        }else{
            e->evt = 0;
        }
    }
    return n;
#endif

    return 0;
}

LP_API inline uint32_t
lpCountSocketPoll(LPSocketPoll * poll){
    return poll->setsCount;
}
